/*
 * Copyright (C) 2009 aileron.cc
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package cc.aileron.anime.logic;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.Iterator;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageTypeSpecifier;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.metadata.IIOInvalidTreeException;
import javax.imageio.metadata.IIOMetadata;
import javax.imageio.metadata.IIOMetadataNode;
import javax.imageio.stream.ImageOutputStream;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cc.aileron.anime.ApplicationConfig;

import com.google.inject.Inject;
import com.google.inject.Singleton;

/**
 * @author Aileron
 */
@Singleton
public class AnimationWriterImpl implements AnimationWriter
{
    @Override
    public void write() throws IOException
    {
        final int countMax = this.status.count();
        logger.debug("write anime [{}]", countMax);

        if (countMax == 0)
        {
            return;
        }

        final Iterator<ImageWriter> it = ImageIO.getImageWritersByFormatName("gif");
        if (it.hasNext() == false)
        {
            throw new Error("not gif writer");
        }
        final ImageWriter writer = it.next();
        final ImageOutputStream out = ImageIO.createImageOutputStream(new File(
                targetImageDir + "para2anime.gif"));
        writer.setOutput(out);

        // write
        final ImageWriteParam writeParam = writer.getDefaultWriteParam();
        writer.prepareWriteSequence(null);

        firstImageWrite(writer, writeParam);

        for (int count = 1; count < countMax; count++)
        {
            final String fileName = targetImageDir + count + ".jpeg";

            logger.debug("read image {}", fileName);

            final BufferedImage image = ImageIO.read(new File(fileName));
            setAttribute(image, writer);

            // write
            writer.writeToSequence(new IIOImage(image, null, null), writeParam);
        }

        writer.endWriteSequence();
        out.close();

        logger.debug("output complete!!!");
    }

    /**
     * @param writer
     * @throws IOException
     */
    private void firstImageWrite(final ImageWriter writer,
            final ImageWriteParam writeParam) throws IOException
    {
        final String fileName = targetImageDir + 0 + ".jpeg";
        logger.debug("read image {}", fileName);
        final BufferedImage image = ImageIO.read(new File(fileName));
        setAttribute(image, writer);

        // write
        writer.writeToSequence(new IIOImage(image, null, null), writeParam);
    }

    /**
     * @param image
     * @param writer
     * @param writeParam
     * @throws IIOInvalidTreeException
     */
    private void setAttribute(final BufferedImage image,
            final ImageWriter writer) throws IIOInvalidTreeException
    {
        // play speed
        final IIOMetadata meta = writer.getDefaultImageMetadata(
                ImageTypeSpecifier.createFromRenderedImage(image), null);
        final String format = meta.getNativeMetadataFormatName();
        final IIOMetadataNode root = (IIOMetadataNode) meta.getAsTree(format);
        final IIOMetadataNode node = new IIOMetadataNode(
                "GraphicControlExtension");
        node.setAttribute("disposalMethod", "none");
        node.setAttribute("userInputFlag", "FALSE");
        node.setAttribute("transparentColorFlag", "FALSE");
        node.setAttribute("transparentColorIndex", "0");
        node.setAttribute("delayTime", sleepTime);
        root.appendChild(node);

        // loop
        final int loop = 0;
        final byte[] data = { 0x01, (byte) ((loop >> 0) & 0xFF),
                (byte) ((loop >> 8) & 0xFF) };
        final IIOMetadataNode list = new IIOMetadataNode(
                "ApplicationExtensions");
        final IIOMetadataNode ext = new IIOMetadataNode("ApplicationExtension");
        ext.setAttribute("applicationID", "NETSCAPE");
        ext.setAttribute("authenticationCode", "2.0");
        ext.setUserObject(data);
        list.appendChild(ext);
        root.appendChild(list);

        // root
        meta.setFromTree(format, root);
    }

    /**
     * @param status
     * @param config
     */
    @Inject
    public AnimationWriterImpl(
        final SnapshotStatus status,
        final ApplicationConfig config)
    {
        this.status = status;
        this.targetImageDir = config.targetImageDir;
        this.sleepTime = String.valueOf(config.sleepTime / 10);
    }

    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    private final String sleepTime;
    private final SnapshotStatus status;
    private final String targetImageDir;
}
